﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Collections;
using System.IO;
using System.Globalization;
using Microsoft.Xna.Framework.Content;

namespace uam_fps_game.Gfx
{

    public class ModelObject
    {
        public Model Model { get; protected set; }

        // Tablica materialow dla kazdego MeshParta (czesci) modelu.
        protected Material[] mMeshPartMaterials;

        // Liczba MeshPartow modelu.
        protected int mMeshPartNumber;

        public int MeshPartCount { get { return mMeshPartNumber; } private set { mMeshPartNumber = value; } }

        public BoundingSphere ObjectSpaceBoundingSphere { get; set; }

        // Tablica macierzy opisujacych przeksztalcenia, gdy model zawiera kosci. Nieistotne na razie, w kodzie znajduje sie
        // dla kompletnosci (gdyby probowano tym kodem ladowac model, ktory kosci zawiera).
        public Matrix[] BoneTransforms { get; protected set; }

        internal ModelObject(string modelFilePath, ContentManager content, bool forceOpaque = false)
        {
            Model = content.Load<Model>(modelFilePath);

            // Patrz opis pola transforms.
            BoneTransforms = new Matrix[Model.Bones.Count];
            Model.CopyAbsoluteBoneTransformsTo(BoneTransforms);


            BoundingSphere objSpaceBSphere = new BoundingSphere();
            foreach (ModelMesh mesh in Model.Meshes)
            {
                objSpaceBSphere = BoundingSphere.CreateMerged(objSpaceBSphere,
                    mesh.BoundingSphere.Transform(BoneTransforms[mesh.ParentBone.Index]));
            }
            ObjectSpaceBoundingSphere = objSpaceBSphere;

            // Zliczanie MeshPartow w modelu (model jest podzielony na Meshe, kazdy Mesh ma MeshParty).
            mMeshPartNumber = 0;
            foreach (ModelMesh mesh in Model.Meshes)
            {
                mMeshPartNumber += mesh.MeshParts.Count;
            }

            // Tworzenie tablicy na materialy MeshPartow
            mMeshPartMaterials = new Material[mMeshPartNumber];

            int meshPartIndex = 0;
            foreach (ModelMesh mesh in Model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    BasicEffect effect = (BasicEffect)part.Effect;

                    mMeshPartMaterials[meshPartIndex] = new Material();

                    mMeshPartMaterials[meshPartIndex].DiffuseColor = new Vector4(effect.DiffuseColor.X, effect.DiffuseColor.Y, effect.DiffuseColor.Z, effect.Alpha);
                    if (effect.Alpha < 1)
                        mMeshPartMaterials[meshPartIndex].IsTransparent = true;
                    else
                        mMeshPartMaterials[meshPartIndex].IsTransparent = false;

                    mMeshPartMaterials[meshPartIndex].DiffuseTexture = effect.Texture;
                    if (effect.Texture == null)
                        mMeshPartMaterials[meshPartIndex].IsTextured = false;
                    else
                        mMeshPartMaterials[meshPartIndex].IsTextured = true;

                    mMeshPartMaterials[meshPartIndex].SpecularColorPower = new Vector4(effect.SpecularColor, effect.SpecularPower);
                    mMeshPartMaterials[meshPartIndex].AmbientColor = effect.AmbientLightColor;
                    mMeshPartMaterials[meshPartIndex].EmissiveColor = effect.EmissiveColor;

                    meshPartIndex++;

                }
            }
            LoadModelInfo(content, modelFilePath.Substring(modelFilePath.IndexOf('\\') + 1));

            if (forceOpaque)
            {
                foreach (Material mat in mMeshPartMaterials)
                {
                    mat.IsTransparent = false;
                }
            }
        }

        public void SetSingleMaterial(Material material)
        {
            for (int i = 0; i < mMeshPartNumber; ++i)
            {
                mMeshPartMaterials[i] = material;
            }
        }

        public Material GetMeshPartMaterial(int meshPartIndex)
        {
            return mMeshPartMaterials[meshPartIndex];
        }

        public Matrix GetBoneTransform(int boneIndex)
        {
            return BoneTransforms[boneIndex];
        }

        private void LoadModelInfo(ContentManager content, string modelFileName)
        {

            Hashtable loadedTextures = new Hashtable();

            string path = "Content/ModelInfo/I" + modelFileName + ".min";

            string line;
            StreamReader sr;
            try
            {
                sr = new StreamReader(path);
            }
            catch (FileNotFoundException)
            {
                return;
            }

            while ((line = sr.ReadLine()) != null)
            {
                if (line.StartsWith("meshparts "))
                {
                    List<Int32> meshNums = new List<int>();
                    string[] meshes = line.Split();
                    if (meshes[1] == "all")
                    {
                        for (int i = 0; i < mMeshPartNumber; i++)
                        {
                            meshNums.Add(i);
                        }
                    }
                    else
                    {
                        for (int i = 1; i < meshes.Length; i++)
                        {
                            meshNums.Add(Convert.ToInt32(meshes[i]));
                        }
                    }
                    while ((line = sr.ReadLine()) != null && line.Trim().Length > 0)
                    {
                        string[] keyval = line.Split(new string[] { " = " }, StringSplitOptions.RemoveEmptyEntries);
                        keyval[1] = keyval[1].Trim(new char[] { '"' });

                        foreach (Int32 meshNum in meshNums)
                        {
                            switch (keyval[0])
                            {
                                case "DiffuseTexture":
                                    if (keyval[1] != "none")
                                    {
                                        if (!loadedTextures.ContainsKey(keyval[1]))
                                        {
                                            loadedTextures.Add(keyval[1], content.Load<Texture>(keyval[1]));
                                        }
                                        mMeshPartMaterials[meshNum].DiffuseTexture = (Texture)loadedTextures[keyval[1]];
                                        mMeshPartMaterials[meshNum].IsTextured = true;
                                    }
                                    break;
                                case "NormalTexture":
                                    if (keyval[1] != "none")
                                    {
                                        if (!loadedTextures.ContainsKey(keyval[1]))
                                        {
                                            loadedTextures.Add(keyval[1], content.Load<Texture>(keyval[1]));
                                        }
                                        mMeshPartMaterials[meshNum].NormalTexture = (Texture)loadedTextures[keyval[1]];
                                        mMeshPartMaterials[meshNum].IsNormalMapped = true;
                                    }
                                    break;
                                case "SpecularTexture":
                                    if (keyval[1] != "none")
                                    {
                                        if (!loadedTextures.ContainsKey(keyval[1]))
                                        {
                                            loadedTextures.Add(keyval[1], content.Load<Texture>(keyval[1]));
                                        }
                                        mMeshPartMaterials[meshNum].SpecularTexture = (Texture)loadedTextures[keyval[1]];
                                        mMeshPartMaterials[meshNum].IsSpecularMapped = true;
                                    }
                                    break;
                                case "ParallaxTexture":
                                    if (keyval[1] != "none")
                                    {
                                        if (!loadedTextures.ContainsKey(keyval[1]))
                                        {
                                            loadedTextures.Add(keyval[1], content.Load<Texture>(keyval[1]));
                                        }
                                        mMeshPartMaterials[meshNum].ParallaxTexture = (Texture)loadedTextures[keyval[1]];
                                        mMeshPartMaterials[meshNum].IsParallaxMapped = true;
                                    }
                                    break;
                                case "ParallaxScaleBias":
                                    string[] sb = keyval[1].Split();
                                    mMeshPartMaterials[meshNum].ParallaxScaleBias = new Vector2(Convert.ToSingle(sb[0], CultureInfo.InvariantCulture), Convert.ToSingle(sb[1], CultureInfo.InvariantCulture));
                                    break;
                                case "IsTransparent":
                                    mMeshPartMaterials[meshNum].IsTransparent = Convert.ToBoolean(keyval[1]);
                                    break;
                                case "SpecularColor":
                                    string[] rgb = keyval[1].Split();
                                    mMeshPartMaterials[meshNum].SpecularColorPower = new Vector4(Convert.ToSingle(rgb[0], CultureInfo.InvariantCulture), Convert.ToSingle(rgb[1], CultureInfo.InvariantCulture), Convert.ToSingle(rgb[2], CultureInfo.InvariantCulture), mMeshPartMaterials[meshNum].SpecularColorPower.W);
                                    break;
                                case "SpecularPower":
                                    mMeshPartMaterials[meshNum].SpecularColorPower = new Vector4(mMeshPartMaterials[meshNum].SpecularColorPower.X, mMeshPartMaterials[meshNum].SpecularColorPower.Y, mMeshPartMaterials[meshNum].SpecularColorPower.Z, Convert.ToSingle(keyval[1], CultureInfo.InvariantCulture));
                                    break;
                            }
                        }
                    }
                }

            }

        }
    }
}
